package pl.gigiel.behaviour.steer;

import pl.gigiel.behaviour.IBoid;
import pl.gigiel.behaviour.ISteer;
import pl.gigiel.behaviour.exception.BehaviourException;
import pl.gigiel.behaviour.util.Util;

import com.jme.math.Vector3f;

public class WanderSphereSteer implements ISteer {
	private static float MIN = -0.1f;
	private static float MAX = 0.1f;
	private static ISteer steer;

	private WanderSphereSteer() {
	}

	public static ISteer getInstance() {
		if (steer == null) {
			steer = new WanderSphereSteer();
		}
		return steer;
	}

	@Override
	public void init() throws BehaviourException {
		// nothing
	}

	@Override
	public Vector3f force(IBoid boid, float elapsed) throws BehaviourException {
		float size = boid.getFeatures().getSize().getX();
		float radius = 2f * size;
		float distance = 4f * size;
		float jitter = 0.2f * size;
		float perseverance = 0f;
		float perseveranceSum = 10f * size;
		Vector3f spherePoint = toSpherePoint(Vector3f.UNIT_Y, radius);
		//
		float stepX = step(jitter, elapsed);
		float stepY = step(jitter, elapsed);
		float stepZ = step(jitter, elapsed);
		spherePoint.addLocal(stepX, stepY, stepZ);
		spherePoint.normalizeLocal();
		spherePoint.multLocal(radius);

		Vector3f offset = spherePoint.add(0f, distance, 0f);
		offset.normalizeLocal();
		perseverance += elapsed;
		if (perseverance > perseveranceSum) {
			perseverance = 0f;
			spherePoint.set(0f, radius, 0f);
		}
		return offset;
	}

	private static Vector3f toSpherePoint(Vector3f target, float radius) {
		Vector3f spherePoint = target.clone();
		if (spherePoint.length() != 0) {
			spherePoint.normalizeLocal();
			spherePoint.multLocal(radius);
		} else {
			spherePoint.set(0f, radius, 0f);
		}
		return spherePoint;
	}

	private static float step(float jitter, float elapsed) {
		float jitterByElapsed = jitter * elapsed;
		float min = jitterByElapsed * MIN;
		float max = jitterByElapsed * MAX;
		return Util.random(min, max);
	}
}
